package avicit.bdp.dds.server.worker.task.standard;

import avicit.bdp.common.service.dto.DataSourceDTO;
import avicit.bdp.common.service.service.DataSourceService;
import avicit.bdp.common.utils.SpringApplicationContext;
import avicit.bdp.common.utils.database.DBUtils;
import avicit.bdp.common.utils.database.adapter.BaseDBAdapter;
import avicit.bdp.common.utils.database.adapter.DbAdapterUtils;
import avicit.bdp.core.enums.DataElementDataTypeEnum;
import avicit.bdp.core.util.json.JSONUtils;
import avicit.bdp.dds.common.Constants;
import avicit.bdp.dds.dispatch.task.AbstractParameters;
import avicit.bdp.dds.dispatch.task.standard.DataElementEvaluateTaskParameters;
import avicit.bdp.dds.server.entity.TaskExecutionContext;
import avicit.bdp.dds.server.worker.task.AbstractTask;
import avicit.bdp.dgs.api.DataElementApi;
import avicit.bdp.dgs.api.DataElementEvaluateTaskApi;
import avicit.bdp.dgs.api.LookupTableValueApi;
import avicit.bdp.dgs.api.MetadataTableApi;
import avicit.bdp.dgs.metadata.vo.DataElementVo;
import avicit.bdp.dgs.metadata.vo.MetadataTableColumnVo;
import avicit.bdp.dgs.standard.DataElementEvaluateTaskResultDTO;
import avicit.platform6.commons.utils.ComUtil;
import com.google.common.collect.Lists;
import java.sql.Connection;
import java.sql.JDBCType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;

/**
 * 数据项标准评估任务
 *
 * @author mayanj
 */
public class DataElementEvaluateTask extends AbstractTask {

  private DataElementEvaluateTaskParameters dataElementEvaluateTaskParameters;

  /** taskExecutionContext */
  private final TaskExecutionContext taskExecutionContext;

  private final DataElementApi dataElementApi;
  private final MetadataTableApi metadataTableApi;
  private final LookupTableValueApi lookupTableValueApi;
  private final DataElementEvaluateTaskApi dataElementEvaluateTaskApi;
  private final DataSourceService dataSourceService;

  /**
   * constructor
   *
   * @param taskExecutionContext taskExecutionContext
   * @param logger logger
   */
  public DataElementEvaluateTask(TaskExecutionContext taskExecutionContext, Logger logger) {
    super(taskExecutionContext, logger);
    this.taskExecutionContext = taskExecutionContext;
    this.dataElementApi = SpringApplicationContext.getBean(DataElementApi.class);
    this.metadataTableApi = SpringApplicationContext.getBean(MetadataTableApi.class);
    this.lookupTableValueApi = SpringApplicationContext.getBean(LookupTableValueApi.class);
    this.dataSourceService = SpringApplicationContext.getBean(DataSourceService.class);
    this.dataElementEvaluateTaskApi =
        SpringApplicationContext.getBean(DataElementEvaluateTaskApi.class);
  }

  @Override
  public void init() {
    logger.info("data element evaluate parameters: {}", taskExecutionContext.getTaskParams());
    dataElementEvaluateTaskParameters =
        JSONUtils.parseObject(
            taskExecutionContext.getTaskParams(), DataElementEvaluateTaskParameters.class);
    if (dataElementEvaluateTaskParameters == null
        || !dataElementEvaluateTaskParameters.checkParameters()) {
      throw new RuntimeException("data element evaluate parameters is not valid");
    }
  }

  /**
   * run task
   *
   * @throws Exception if error throws Exception
   */
  @Override
  public void handle() throws Exception {
    try {
      // 处理标准评估
      handleDataElementEvaluate();
      setExitStatusCode(Constants.EXIT_CODE_SUCCESS);
    } catch (Exception e) {
      logger.error("标准评估任务异常：", e);
      setExitStatusCode(Constants.EXIT_CODE_FAILURE);
      throw e;
    }
  }

  private void handleDataElementEvaluate() {
    // 处理数据元素评估
    String dataElementIds = dataElementEvaluateTaskParameters.getDataElementIds();
    if (StringUtils.isNotBlank(dataElementIds)) {
      List<String> dataElementIdList = Lists.newArrayList(dataElementIds.split(Constants.COMMA));
      // 查询数据项标准
      for (String dataElementId : dataElementIdList) {
        DataElementVo dataElementVo = dataElementApi.queryById(dataElementId);
        if (dataElementVo == null) {
          logger.error("ID为{}的数据项不存在。", dataElementId);
          continue;
        }

        String dataElementDataType =
            DataElementDataTypeEnum.getDescByCode(dataElementVo.getDataType());
        if (StringUtils.isBlank(dataElementDataType)) {
          logger.error("数据项标准数据类型为空。");
          continue;
        }

        // 查询数据项标准关联的列
        List<MetadataTableColumnVo> columnList =
            metadataTableApi.getColumnList(null, dataElementId, null);
        if (CollectionUtils.isEmpty(columnList)) {
          logger.error("ID为{}的数据项没有关联的列", dataElementId);
          continue;
        }

        // 获取值域范围
        List<String> valueList = null;
        String lookupTableId = dataElementVo.getLookupTableId();
        if (StringUtils.isNotBlank(lookupTableId)) {
          valueList = lookupTableValueApi.getValueList(lookupTableId);
        }

        Integer dataElementDataLength = dataElementVo.getLength();
        Integer dataElementDecimalPrecision = dataElementVo.getDecimalPrecision();
        Integer dataElementUniqueKey = dataElementVo.getUniqueKey();
        Integer dataElementNotNull = dataElementVo.getNotNull();

        List<String> errorMsg = new ArrayList<>();

        // 评估标准
        for (MetadataTableColumnVo metadataTableColumnVo : columnList) {

          errorMsg.clear();

          boolean validate = true;
          // 1.检验字段类型
          String datasourceType = metadataTableColumnVo.getDatasourceType();
          String columnType = metadataTableColumnVo.getColumnType();
          logger.info("开始评估字段: {}", metadataTableColumnVo);

          if (StringUtils.isAnyEmpty(datasourceType, columnType)) {
            validate = false;
            errorMsg.add(String.format("字段%s数据类型为空,数据类型不符合标准！", metadataTableColumnVo.getName()));
          } else {
            try {
              BaseDBAdapter dbAdapter = DbAdapterUtils.getDbAdapter(datasourceType);
              Integer javaType = dbAdapter.convertJavaType(columnType);
              String columnJdbcName = JDBCType.valueOf(javaType).getName();
              if (!dataElementDataType.equalsIgnoreCase(columnJdbcName)) {
                validate = false;
                errorMsg.add(
                    String.format(
                        "字段%s数据类型为%s,标准数据类型为%s,数据类型不符合标准！",
                        metadataTableColumnVo.getName(), columnType, dataElementDataType));
              }
            } catch (Exception e) {
              logger.error("数据类型检验异常：", e);
            }
          }

          // 2.检验字段长度
          Integer columnLength = metadataTableColumnVo.getColumnLength();
          if (columnLength != null
              && dataElementDataLength != null
              && columnLength > dataElementDataLength) {
            validate = false;
            errorMsg.add(
                String.format(
                    "字段%s数据长度为%s,标准数据长度为%s,数据长度不符合标准！",
                    metadataTableColumnVo.getName(), columnLength, dataElementDataLength));
          }

          // 3.检验字段精度
          Integer columnPrecision = metadataTableColumnVo.getColumnPrecision();
          if (columnPrecision != null
              && dataElementDecimalPrecision != null
              && !columnPrecision.equals(dataElementDecimalPrecision)) {
            validate = false;
            errorMsg.add(
                String.format(
                    "字段%s数据精度为%s,标准数据精度为%s,数据精度不符合标准！",
                    metadataTableColumnVo.getName(), columnPrecision, dataElementDecimalPrecision));
          }

          // 4.检验字段是否为空
          Integer nullable = metadataTableColumnVo.getNullable();
          if (nullable != null
              && dataElementNotNull != null
              && !nullable.equals(dataElementNotNull)) {
            validate = false;
            errorMsg.add(String.format("字段%s可为空不符合标准！", metadataTableColumnVo.getName()));
          }

          // 5.检验字段唯一性
          Integer uniqueKey = metadataTableColumnVo.getUniqueKey();
          if (uniqueKey != null
              && dataElementUniqueKey != null
              && !uniqueKey.equals(dataElementUniqueKey)) {
            validate = false;
            errorMsg.add(String.format("字段%s唯一性不符合标准！", metadataTableColumnVo.getName()));
          }

          // 6.检验值域标准
          if (CollectionUtils.isNotEmpty(valueList)) {
            if (StringUtils.isEmpty(metadataTableColumnVo.getDatasourceId())) {
              validate = false;
              errorMsg.add(String.format("字段%s数据源为空！", metadataTableColumnVo.getName()));
            } else {
              String sql =
                  "select count(1) countValue from "
                      + metadataTableColumnVo.getTableName()
                      + " where "
                      + metadataTableColumnVo.getName()
                      + " not in ("
                      + StringUtils.join(valueList, ",")
                      + ")";

              DataSourceDTO dataSourceDTO =
                  dataSourceService.queryDataSourceById(metadataTableColumnVo.getDatasourceId());
              if (dataSourceDTO == null) {
                logger.error("数据源不存在，数据源ID={}", metadataTableColumnVo.getDatasourceId());
                validate = false;
                errorMsg.add(
                    String.format("数据源不存在，数据源ID为%s！", metadataTableColumnVo.getDatasourceId()));
              } else {
                Connection conn = null;
                try {
                  conn = DBUtils.buildConnectionFromPool(DBUtils.getBaseDataSource(dataSourceDTO));
                  List<Map<String, Object>> result = DBUtils.getMapsBySql(sql, conn);
                  if (CollectionUtils.isNotEmpty(result)) {
                    Map<String, Object> map = result.get(0);
                    int intValue = MapUtils.getIntValue(map, "countValue".toLowerCase(), 0);
                    if (intValue > 0) {
                      validate = false;
                      errorMsg.add(String.format("字段%s值域不符合标准！", metadataTableColumnVo.getName()));
                    }
                  }
                } catch (Exception e) {
                  logger.error("检测字段值域标准异常", e);
                } finally {
                  DBUtils.closeResource(conn, null, null);
                }
              }
            }

            // 7.保存检测结果
            if (!validate) {
              logger.error(
                  "字段{}校验不符合标准：{}",
                  metadataTableColumnVo.getName(),
                  StringUtils.join(errorMsg, "\n"));
            }

            DataElementEvaluateTaskResultDTO resultDTO = new DataElementEvaluateTaskResultDTO();
            resultDTO.setId(ComUtil.getId());
            resultDTO.setEvaluateTaskId(dataElementEvaluateTaskParameters.getTaskId());
            resultDTO.setDataElementId(dataElementId);
            resultDTO.setResultMsg(StringUtils.join(errorMsg, "\n"));
            resultDTO.setResult(validate ? 1 : 0);
            resultDTO.setTaskInstanceId(taskExecutionContext.getTaskInstanceId());
            resultDTO.setMetadataColumnId(metadataTableColumnVo.getId());

            try {
              dataElementEvaluateTaskApi.saveElementEvaluateTaskResult(resultDTO);
            } catch (Exception e) {
              logger.error("保存检测结果异常", e);
            }
          }
        }
      }
    }
  }

  @Override
  public AbstractParameters getParameters() {
    return dataElementEvaluateTaskParameters;
  }
}
